//! Simple GPIO button test.
//!
//! Wire a momentary button so that one leg goes to pin 9 (GND) and the
//! other leg connects to pin 11, which is GPIO1_A4.

use axplat::mem::{pa, phys_to_virt};
use axplat_aarch64_opi5p::gpio_controller;
use gpio_rk3588::Level;

/// GPIO bank index for GPIO1.
const BUTTON_BANK: usize = 1;
/// Pin offset within GPIO1 for A4.
const BUTTON_PIN: u8 = 4;

/// VCCIO1_4 IOC block base address (provides pull configuration for GPIO1).
const VCCIO1_4_IOC_BASE: usize = 0xFD5F9000;
/// Pull configuration register offset for GPIO1_A0..A7 group.
const GPIO1A_PULL_OFFSET: usize = 0x0110;
/// Number of pins packed into a single pull register.
const PULL_PINS_PER_REG: u32 = 8;
/// Bits used per pin in the pull register.
const PULL_BITS_PER_PIN: u32 = 2;
/// Encoded pull-up value for PULL_TYPE_IO_1V8_ONLY (per RK3588 TRM / Linux pinctrl).
const PULL_UP_CODE: u32 = 0b11;

fn enable_internal_pullup() {
    use core::ptr::{read_volatile, write_volatile};

    let pull_reg_phys = VCCIO1_4_IOC_BASE + GPIO1A_PULL_OFFSET;
    let pull_reg = phys_to_virt(pa!(pull_reg_phys)).as_usize();

    let pin_index = (BUTTON_PIN as u32) % PULL_PINS_PER_REG;
    let bit_shift = pin_index * PULL_BITS_PER_PIN;
    let mask = ((1u32 << PULL_BITS_PER_PIN) - 1) << (bit_shift + 16);
    let value = PULL_UP_CODE << bit_shift;
    let data = mask | value;

    let before = unsafe { read_volatile(pull_reg as *const u32) };
    info!(
        "Configuring GPIO1_A4 pull-up: reg=0x{:08X}, before=0x{:08X}, write=0x{:08X}",
        pull_reg_phys, before, data
    );

    unsafe {
        write_volatile(pull_reg as *mut u32, data);
    }

    let after = unsafe { read_volatile(pull_reg as *const u32) };
    info!(
        "GPIO1_A4 pull register after write: 0x{:08X} (bits {}..{} now {}b)",
        after,
        bit_shift + 1,
        bit_shift,
        (after >> bit_shift) & ((1 << PULL_BITS_PER_PIN) - 1)
    );
}

/// Poll the button input and log when a press (High → Low) is observed.
pub fn run_gpio_button_test() {
    let gpio = gpio_controller();

    enable_internal_pullup();

    let mut last_level = gpio.read_input(BUTTON_BANK, BUTTON_PIN);
    info!(
        "Waiting for button press on GPIO1_A4 (board pin 11). Connect the other leg to GND (pin 9). Idle level: {:?}",
        last_level
    );
    info!("Press the button to pull the line low; logging once a high→low transition is detected.");

    loop {
        let level = gpio.read_input(BUTTON_BANK, BUTTON_PIN);

        if level == Level::Low && last_level == Level::High {
            info!("Button press detected on GPIO1_A4!");
            break;
        }

        last_level = level;

        // Short delay to avoid hammering the bus with reads.
        for _ in 0..10 {
            core::hint::spin_loop();
        }
    }
}
